use std::sync::Arc;

use axum::{
    extract::{Path, Query, State},
    Json,
};
use chrono::Local;
use sqlx::PgPool;
use validator::Validate;

use crate::{
    db, filter, form,
    handler::{get_conn, log_error, AffResponse, IDResponse, Response},
    model, service, utils, AppState, Error, Result,
};

pub async fn list(
    State(state): State<Arc<AppState>>,
    Query(frm): Query<form::user::List>,
) -> Result<Json<Response<db::Pagination<model::user::User>>>> {
    let handler_name = "admin/user/list";

    frm.validate()
        .map_err(Error::from)
        .map_err(log_error(handler_name))?;

    let pool = get_conn(&state);

    let data = service::user::list(
        &pool,
        &filter::user::List {
            pagination: filter::Pagination {
                page: frm.p.page(),
                ..Default::default()
            },
            ..Default::default()
        },
    )
    .await
    .map_err(log_error(handler_name))?;

    Ok(Response::ok(data).to_json())
}
pub async fn add(
    State(state): State<Arc<AppState>>,
    Json(frm): Json<form::user::Add>,
) -> Result<Json<Response<IDResponse>>> {
    let handler_name = "admin/user/list";

    frm.validate()
        .map_err(Error::from)
        .map_err(log_error(handler_name))?;

    if &frm.password != &frm.re_password {
        return Err(Error::invalid_parameter("两次输入的密码不一致"));
    }

    let password = utils::password::hash(&frm.password).map_err(log_error(handler_name))?;

    let pool = get_conn(&state);

    let m = model::user::User {
        email: frm.email,
        password,
        status: model::user::Status::Actived,
        role: model::user::Role::Member,
        dateline: Local::now(),
        ..Default::default()
    };

    let id = service::user::add(&pool, &m)
        .await
        .map_err(log_error(handler_name))?;

    Ok(Response::ok(IDResponse { id }).to_json())
}
pub async fn edit(
    State(state): State<Arc<AppState>>,
    Json(frm): Json<form::user::Edit>,
) -> Result<Json<Response<AffResponse>>> {
    let handler_name = "admin/user/edit";

    frm.validate()
        .map_err(Error::from)
        .map_err(log_error(handler_name))?;

    if let Some(pwd) = &frm.password {
        if !frm.skip {
            match &frm.re_password {
                Some(v) => {
                    if pwd.len() < 6 {
                        return Err(Error::invalid_parameter("密码至少6个字符"));
                    }
                    if pwd != v {
                        return Err(Error::invalid_parameter("两次输入的密码不一样"));
                    }
                }
                None => return Err(Error::invalid_parameter("两次输入的密码不一样")),
            };
        }
    }

    let password = match &frm.password {
        Some(v) if !frm.skip => utils::password::hash(v).map_err(log_error(handler_name))?,

        _ => "".to_string(),
    };

    let pool = get_conn(&state);

    let m = model::user::User {
        id: frm.id,
        email: frm.email,
        password,
        status: model::user::Status::Actived,
        role: model::user::Role::Member,
        dateline: Local::now(),
        ..Default::default()
    };

    let rows = service::user::edit(&pool, &m, frm.skip)
        .await
        .map_err(log_error(handler_name))?;

    Ok(Response::ok(AffResponse { rows }).to_json())
}

pub async fn freeze(
    State(state): State<Arc<AppState>>,
    Json(frm): Json<form::user::IDAction>,
) -> Result<Json<Response<AffResponse>>> {
    let handler_name = "admin/user/freeze";

    frm.validate()
        .map_err(Error::from)
        .map_err(log_error(handler_name))?;

    let pool = get_conn(&state);

    let data = change_status(&pool, &frm.id, model::user::Status::Freezed)
        .await
        .map_err(log_error(handler_name))?;

    Ok(Response::ok(data).to_json())
}

pub async fn active(
    State(state): State<Arc<AppState>>,
    Json(frm): Json<form::user::IDAction>,
) -> Result<Json<Response<AffResponse>>> {
    let handler_name = "admin/user/active";

    frm.validate()
        .map_err(Error::from)
        .map_err(log_error(handler_name))?;

    let pool = get_conn(&state);

    let data = change_status(&pool, &frm.id, model::user::Status::Actived)
        .await
        .map_err(log_error(handler_name))?;

    Ok(Response::ok(data).to_json())
}
pub async fn pending(
    State(state): State<Arc<AppState>>,
    Json(frm): Json<form::user::IDAction>,
) -> Result<Json<Response<AffResponse>>> {
    let handler_name = "admin/user/pending";

    frm.validate()
        .map_err(Error::from)
        .map_err(log_error(handler_name))?;

    let pool = get_conn(&state);

    let data = change_status(&pool, &frm.id, model::user::Status::Pending)
        .await
        .map_err(log_error(handler_name))?;

    Ok(Response::ok(data).to_json())
}

async fn change_status(
    pool: &PgPool,
    id: &str,
    status: model::user::Status,
) -> Result<AffResponse> {
    let rows = service::user::change_status(&pool, id, &status).await?;
    Ok(AffResponse { rows })
}

pub async fn del(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
) -> Result<Json<Response<AffResponse>>> {
    let handler_name = "admin/user/del";

    let pool = get_conn(&state);
    let rows = service::user::del(&pool, &id)
        .await
        .map_err(log_error(handler_name))?;
    Ok(Response::ok(AffResponse { rows }).to_json())
}
